package com.ylr.base.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Function;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-09-12 10:01:09
 * className: TreeUtils 结构树生成工具类
 * version: 1.0
 * description:
 */
public class TreeUtils {

    private static final Logger log = LoggerFactory.getLogger(TreeUtils.class);

    /**
     * 顶级节点的key值
     */
    private static final Long TOP_PARENT_KEY = 0L;

    /**
     * 结构树构造方法
     * @param list              元素列表
     * @param keyFunction       元素key生成方法
     * @param parentKeyFunction 父级元素key生成方法
     * @param changeDtoFunction 元素转换实现方法
     * @param childrenFunction  元素存储的子元素集合获取方法
     * @return                  结果列表
     * @param <T>               元素值
     * @param <K>               键值
     * @param <R>               元素转换结果值
     */
    public static <T, K, R> List<R> buildTree(List<T> list, Function<T, K> keyFunction, Function<T, K> parentKeyFunction,
                                              Function<T, R> changeDtoFunction, Function<R, TreeSet<R>> childrenFunction) {
        // 结果列表
        List<R> result = new ArrayList<>();
        // 中间结果缓存
        Map<K, R> map = new HashMap<>();
        // 延后处理的元素
        List<T> lastOptList = new ArrayList<>();

        for (T t : list) {
            // 1、数据转换
            R r = changeDtoFunction.apply(t);

            // 2、缓存到映射中
            map.put(keyFunction.apply(t), r);

            // 3、获取父节点
            R parent = map.get(parentKeyFunction.apply(t));
            if (Objects.isNull(parent)) {
                if (TOP_PARENT_KEY.equals(parentKeyFunction.apply(t))) {
                    // 3.1、一级节点，直接添加到结果列表中
                    result.add(r);
                } else {
                    // 3.2、延后处理
                    lastOptList.add(t);
                }
                continue;
            }
            // 4、获取父节点对象存储的子节点集合
            TreeSet<R> children = childrenFunction.apply(parent);
            if (Objects.isNull(children)) {
                log.error("获取父节点存储的子节点集合失败。parent={}", parent);
                continue;
            }
            // 添加到子节点集合中
            children.add(r);
        }
        // 开始处理剩余元素
        for (T t : lastOptList) {
            // 5、获取父节点
            R parent = map.get(parentKeyFunction.apply(t));
            if (Objects.isNull(parent)) {
                log.error("父级节点数据不存在，直接过滤该节点数据。{}", t);
                continue;
            }
            // 6、获取父节点对象存储的子节点集合
            TreeSet<R> children = childrenFunction.apply(parent);
            if (Objects.isNull(children)) {
                log.error("获取父节点存储的子节点集合失败。parent={}", parent);
                continue;
            }

            // 7、获取节点的数据转换对象
            R r = map.get(keyFunction.apply(t));

            // 8、添加到子节点集合中
            children.add(r);
        }
        // 9、返回结果
        return result;
    }

    /*public static void main(String[] args) {
        Function<Dept, Long> keyFunction = Dept::getId;
        Function<Dept, Long> parentKeyFunction = Dept::getParentId;
        Function<DeptDto, TreeSet<DeptDto>> childrenFunction = DeptDto::initAndGet;
        Function<Dept, DeptDto> changeDtoFunction = dept -> {
            DeptDto deptDto = new DeptDto();
            deptDto.setId(dept.getId().toString());
            deptDto.setParentId(dept.getParentId().toString());
            deptDto.setChildren(new TreeSet<>());
            return deptDto;
        };

        List<Dept> list = new ArrayList<>();

        List<DeptDto> result = TreeUtils.buildTree(list, keyFunction, parentKeyFunction, changeDtoFunction, childrenFunction);

    }

    @Getter
    @Setter
    private static class Dept {

        private Long id;

        private Long parentId;

    }

    @Getter
    @Setter
    private static class DeptDto implements Comparable<DeptDto> {

        private String id;

        private String parentId;

        private TreeSet<DeptDto> children;

        public TreeSet<DeptDto> initAndGet() {
            if (Objects.isNull(children)) {
                children = new TreeSet<>();
            }
            return children;
        }

        @Override
        public int compareTo(DeptDto o) {
            return Long.valueOf(id).compareTo(Long.valueOf(o.getId()));
        }
    }*/

}
